﻿/*===================================================
* CLR版本  : 4.0.30319.42000 
* 类名称   : ObjectEvents
* 文 件 名 : ObjectEvents 
* 类描述   :
* 创建人   : oream
* 创建时间 : 2018/2/9 16:01:22
* 修改人   : 
* 修改时间 :
* 版本： @version 1.0
=====================================================*/

using System;
using System.Collections.Generic;
using Model;
using UnityEngine;

namespace Hotfix
{
  public interface IObjectSystem
  {
    Type Type();
    void Set(object value);
  }

  public class ObjectSystem<T> : IObjectSystem
  {
    private T value;
    public T Get()
    {
      return this.value;
    }

    public void Set(object value)
    {
      this.value = (T)value;
    }

    public Type Type()
    {
      return typeof(T);
    }
  }


  public sealed class EventSystem
  {
    private readonly Dictionary<Type, IObjectSystem> dicDisposerEvents = new Dictionary<Type, IObjectSystem>();

    private readonly Dictionary<int, List<IEvent>> allEvents = new Dictionary<int, List<IEvent>>();

    private Queue<Disposer> update = new Queue<Disposer>();
    private Queue<Disposer> update2 = new Queue<Disposer>();

    private Queue<Disposer> lateUpdate = new Queue<Disposer>();
    private Queue<Disposer> lateUpdate2 = new Queue<Disposer>();

    private Queue<Disposer> load = new Queue<Disposer>();
    private Queue<Disposer> load2 = new Queue<Disposer>();

    private readonly Queue<Disposer> start = new Queue<Disposer>();

    private readonly HashSet<Disposer> unique = new HashSet<Disposer>();

    public EventSystem()
    {
      this.dicDisposerEvents.Clear();
      this.allEvents.Clear();

      Type[] types = Model.DllHelper.GetDllTypes();

      foreach (Type type in types)
      {
        object[] objs = type.GetCustomAttributes(typeof(ObjectSystemAttribute), false);
        if (objs.Length == 0) continue;

        if (!(objs[0] is ObjectSystemAttribute)) continue;

        object obj = Activator.CreateInstance(type);
        IObjectSystem objectSystem = obj as IObjectSystem;
        if (objectSystem == null)
        {
          Debug.LogError($"{type.Name}组件 没有继承IObjectSystem");
          continue;
        }
        dicDisposerEvents.Add(objectSystem.Type(), objectSystem);
      }

      //foreach (var type in types)
      //{
      //  object[] objs = type.Assembly.GetCustomAttributes(typeof(EventAttribute), false);
      //  foreach (var item in objs)
      //  {
      //    EventAttribute eventAttribute = (EventAttribute)item;
      //    object obj = Activator.CreateInstance(type);
      //    IEvent @event = obj as IEvent;
      //    if (@event == null)
      //    {
      //      Debug.LogError($"{obj.GetType().Name} 没有继承IEvent");
      //      continue;
      //    }
      //    this.RegisterEvent(eventAttribute.Type, @event);
      //    Action<List<object>> action = list => { Handle(eventAttribute.Type, list); };
      //    Hotfix.EventSystem.RegisterEvent(eventAttribute.Type, new EventProxy(action));
      //  }
      //}

      this.Load();
    }

    public void RegisterEvent(int eventID, IEvent e)
    {
      if (!this.allEvents.ContainsKey(eventID))
        this.allEvents.Add(eventID, new List<IEvent>());
      this.allEvents[eventID].Add(e);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="type"></param>
    /// <param name="param"></param>
    public static void Handle(int type, List<object> param)
    {
      switch (param.Count)
      {
        case 0:
          break;
        case 1:
          break;
        case 2:
          break;
        case 3:
          break;
      }
    }

    public void Add(Disposer disposer)
    {
      if (!dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) return;

      if (value is ILoad) this.load.Enqueue(disposer);

      if (value is IUpdate) this.update.Enqueue(disposer);

      if (value is ILateUpdate) this.lateUpdate.Enqueue(disposer);

      if (value is IStart) this.start.Enqueue(disposer);
    }

    public void Awake(Disposer disposer)
    {
      this.Add(disposer);

      if (!dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) return;

      IAwake awake = value as IAwake;

      if (awake == null) return;

      value.Set(disposer);
      awake.Awake();
    }

    public void Awake<T>(Disposer disposer, T t1)
    {
      this.Add(disposer);
      if (!dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) return;

      IAwake<T> awake = value as IAwake<T>;

      if (awake == null) return;

      value.Set(disposer);
      awake.Awake(t1);
    }

    public void Awake<T1, T2>(Disposer disposer, T1 t1, T2 t2)
    {
      this.Add(disposer);

      if (!dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) return;

      IAwake<T1, T2> awake = value as IAwake<T1, T2>;
      if (awake == null) return;

      value.Set(disposer);
      awake.Awake(t1, t2);
    }


    public void Awake<T1, T2, T3>(Disposer disposer, T1 t1, T2 t2, T3 t3)
    {
      this.Add(disposer);

      if (!dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) return;

      IAwake<T1, T2, T3> awake = value as IAwake<T1, T2, T3>;
      if (awake == null) return;

      value.Set(disposer);
      awake.Awake(t1, t2, t3);
    }

    public void Load()
    {
      Debug.Log("进入Load方法");
      unique.Clear();
      while (this.load.Count > 0)
      {
        Disposer disposer = this.load.Dequeue();
        if (disposer.id == 0) continue;
        if (!this.unique.Add(disposer)) continue;
        if (!this.dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) continue;
        this.load2.Enqueue(disposer);

        ILoad load = value as ILoad;
        if (load == null) continue;

        value.Set(disposer);

        try
        {
          load.Load();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
      ObjectHelper.Swap(ref this.load, ref this.load2);
    }

    public void Start()
    {
      this.unique.Clear();
      while (this.start.Count > 0)
      {
        Disposer disposer = this.start.Dequeue();
        if (disposer.id == 0) continue;
        if (!this.unique.Add(disposer)) continue;
        if (!this.dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) continue;

        IStart start = value as IStart;
        if (start == null) continue;
        value.Set(disposer);
        try
        {
          start.Start();
        }
        catch (Exception e)
        {
          Debug.Log(e.ToString());
        }

      }
    }

    public void Update()
    {
      this.unique.Clear();
      while (this.update.Count > 0)
      {
        Disposer disposer = this.update.Dequeue();
        if (disposer.id == 0) continue;
        if (!this.unique.Add(disposer)) continue;
        if (!this.dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) continue;
        this.update2.Enqueue(disposer);

        var iUpdate = value as IUpdate;
        if (iUpdate == null) continue;
        value.Set(disposer);

        try
        {
          iUpdate.Update();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
      ObjectHelper.Swap(ref this.update, ref this.update2);
    }

    public void LateUpdate()
    {
      this.unique.Clear();
      while (this.lateUpdate.Count > 0)
      {
        Disposer disposer = this.lateUpdate.Dequeue();
        if (disposer.id == 0) continue;
        if (!this.unique.Add(disposer)) continue;
        if (!this.dicDisposerEvents.TryGetValue(disposer.GetType(), out IObjectSystem value)) continue;
        this.lateUpdate2.Enqueue(disposer);

        ILateUpdate lateUpdate = value as ILateUpdate;
        if (lateUpdate == null) continue;
        value.Set(disposer);

        try
        {
          lateUpdate.LateUpdate();
        }
        catch (Exception e)
        {
          Debug.LogError(e.ToString());
        }
      }
    }

  }
}
